﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO.MemoryMappedFiles;
using System.IO;
using System.Diagnostics;

namespace KeyboardRemapper
{
   class LowLevelKeyboardHook : NativeWindow
   {
      [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
      static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileNamee);

      [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
      static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

      [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
      static extern bool FreeLibrary(int hModule);

      public Func<int, bool, bool> BlockKey = null;
      delegate void DllMethod();
      delegate bool DllMethodHwnd(IntPtr handle);
      DllMethod RemoveHotkeyHook;
      DllMethodHwnd InstallHotkeyHook;
      MemoryMappedFile mmf = null;
      public LowLevelKeyboardHook()
      {
         CreateHandle(new CreateParams());
         string dllName = Path.Combine(Application.StartupPath, @"keyboardhook.dll");
         int hModule = LoadLibrary(dllName);
         if (hModule == 0)
         {
            FreeLibrary(hModule);
         }
         else
         {
            IntPtr funcPointer = GetProcAddress(hModule, "InstallHotkeyHook");
            if (funcPointer == null)
            {
               FreeLibrary(hModule);
               return;
            }
            InstallHotkeyHook = (DllMethodHwnd)Marshal.GetDelegateForFunctionPointer(funcPointer, typeof(DllMethodHwnd));
            funcPointer = GetProcAddress(hModule, "RemoveHotkeyHook");
            if (funcPointer == null)
            {
               FreeLibrary(hModule);
               return;
            }
            RemoveHotkeyHook = (DllMethod)Marshal.GetDelegateForFunctionPointer(funcPointer, typeof(DllMethod));
         }
         InstallHotkeyHook(Handle);
      }

      public void StopHook()
      {
         RemoveHotkeyHook();
      }

      const int WM_USER = 1024;
      protected override void WndProc(ref Message m)
      {
         if (m.Msg == WM_USER + 755)
         {
            if (mmf == null)
            {
               try
               {
                  mmf = MemoryMappedFile.OpenExisting("KeyRemapper", MemoryMappedFileRights.FullControl);
               }
               catch { }
            }

            MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor();
            if (BlockKey != null)
            {
               int iScanCode= (int)(((Int64)m.LParam >> 16) & 0x1FF);
               if (iScanCode == 256)
               {
                  // Scancode is 0, this is probably a multimedia key generated by some input device
                  // use the vkey instead
                  iScanCode = (int)(512 + (int)m.WParam);
               }

               bool bKeyDown=(((Int64)m.LParam) & 0x80000000L) == 0;
               bool bBlock=BlockKey(iScanCode,bKeyDown );

               KeyInterceptor.DebugLine("Keyboard_LL : " + iScanCode + " Down: " + bKeyDown+"   => Block?"+bBlock);

               if (bBlock)
                  accessor.Write(0, (Int32)1);
               else
                  accessor.Write(0, (Int32)0);
            }
            else
               accessor.Write(0, (Int32)0);
         }
         base.WndProc(ref m);
      }

   }
}
